package com.yy.tech.data.coder.encode;

import com.fasterxml.jackson.databind.util.Converter;
import com.fasterxml.jackson.databind.util.StdConverter;
import com.yy.tech.data.coder.secret.std.Aes256Secret;
import com.yy.tech.data.coder.secret.std.Base64Secret;
import com.yy.tech.data.context.SecretContent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;

/**
 * @author yy
 * @date 2022/4/4
 * @description: TODO
 */
public interface Encoder<IN, OUT> extends Converter<IN, OUT> {

    default OUT encode(IN in){
        return convert(in);
    }


    @Component(SecretContent.ENCODER_BASE64)
    class Base64Encoder extends StdConverter<String, String> implements Encoder<String, String> {

        @Autowired
        private Base64Secret secret;
        @Override
        public String convert(final String str) {
            return secret.encrypt(str);
        }

    }

    @Component(SecretContent.ENCODER_AES256)
    class Aes256Encoder extends StdConverter<String,String> implements  Encoder<String,String>{

        @Autowired
        private Aes256Secret secret;
        @Override
        public String convert(final String s) {
            return secret.encrypt(s);
        }
    }

    @Component(SecretContent.ENCODER_MASK)
    class MaskEncoder extends StdConverter<String,String> implements Encoder<String,String>{

        private int maskLen;
        private int mode;//0 both,1.middle,2 all
        private int charLen;
        private int minLen;
        private String maskChar;
        private String maskStr;
        private String allMaskStr;
        @Autowired
        private Environment env;
        @PostConstruct
        public void initialize(){
            maskChar=getMaskChar();
            maskLen = getMaskLen();
            mode= getMode();
            if (maskLen < 4 || 2==mode){
                maskStr=maskChars(maskLen<4?4:maskLen);
                allMaskStr = maskStr;
                minLen=0;
            }else {
                charLen = Math.floorDiv(maskLen,4);
                minLen = charLen*3;
                if (0==mode){
                    maskStr = maskChars(charLen*2);
                }else if (1 == mode){
                    maskStr = maskChars(charLen);
                }
                allMaskStr = maskChars(maskLen);
            }
        }

        private String maskChars(int len){
            StringBuilder sb= new StringBuilder();
            for (int i=0;i< len;i++){
                sb.append(maskChar);
            }
            return sb.toString();
        }
        @Override
        public String convert(final String value) {
            if (!StringUtils.isEmpty(value)){
                if (minLen ==0 || value.length() < minLen){
                    return allMaskStr;
                }else {
                    if (0==mode){
                        return handleBothSides(value);
                    }else  if (1 == mode){
                        return handleMiddle(value);
                    }else {
                        return allMaskStr;
                    }
                }
            }
            return value;
        }

        private String handleBothSides(String value){
            StringBuilder builder = new StringBuilder();
            builder.append(value.substring(0,charLen));
            builder.append(maskStr);
            builder.append(value.substring(value.length()-charLen));
            return builder.toString();
        }

        private String handleMiddle(String value){
            StringBuilder builder = new StringBuilder();
            builder.append(maskStr);
            int start = Math.floorDiv(value.length()-charLen*2,2);
            builder.append(value.substring(start,start+charLen*2));
            builder.append(maskStr);
            return builder.toString();
        }

        private int getMaskLen(){
            return env.getProperty(SecretContent.KEY_DESENSITIZE_ENCODER_MASK_LEN,Integer.class,16);
        }

        public int getMode() {
            return env.getProperty(SecretContent.KEY_DESENSITIZE_ENCODER_MASK_MODE,Integer.class,0);
        }

        public String getMaskChar() {
            return env.getProperty(SecretContent.KEY_DESENSITIZE_ENCODER_MASK_CHAR,"*");
        }
    }
}
